Conversation
Add a new query CLI to support quick read/debug workflows from terminal and AI agents. - add influx3 query with json, jsonl, csv, and pretty output - add module execution path via python -m influxdb_client_3 - wire console entry point in setup - add CLI tests
bednar
left a comment
There was a problem hiding this comment.
@TheTrueAI thanks for the PR. There is already an existing CLI tool: https://docs.influxdata.com/influxdb3/core/reference/cli/influxdb3/. What’s the reason for creating a new one in the Python v3 client?
|
Good point — the official influxdb3 query CLI does cover the same query functionality. My motivation was zero-install in Python environments: Happy to scope this down or close if you feel the overlap isn't justified. |
Thanks for explanation, every PR is welcome so we will be happy to review your code. Thanks again for your contribution. |
There was a problem hiding this comment.
Pull request overview
Introduces a first-party, scriptable influx3 query CLI for executing SQL/InfluxQL queries against InfluxDB 3, and adds a small Windows-focused TLS root cert compatibility fix for Flight query PEM loading.
Changes:
- Add
influx3console script +python -m influxdb_client_3module entrypoint for query workflows. - Implement query execution + output formatting (JSON/JSONL/CSV/pretty), config/env precedence, and basic error handling.
- Normalize PEM certificate line endings when reading Flight query root certs.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
influxdb_client_3/cli.py |
New CLI implementation for influx3 query including formatting, env precedence, and output handling. |
influxdb_client_3/__main__.py |
Enables python -m influxdb_client_3 CLI execution. |
setup.py |
Adds console_scripts entry point for influx3. |
tests/test_cli.py |
Adds unit tests for CLI parsing and core query execution paths. |
influxdb_client_3/query/query_api.py |
Normalizes PEM line endings when reading root certificates. |
README.md |
Documents CLI usage, formats, and configuration precedence. |
CHANGELOG.md |
Adds feature + bug-fix entries for the CLI and PEM normalization. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| with InfluxDBClient3(host=host, database=database, token=token, **query_kwargs) as client: | ||
| table = client.query( | ||
| query=query, | ||
| language=args.language, | ||
| mode="all", | ||
| database=database, | ||
| ) | ||
|
|
||
| payload = _ensure_trailing_nl(_format_table(table, args.output_format)) |
There was a problem hiding this comment.
The CLI forces mode="all" and then converts the full pa.Table to a Python list (table.to_pylist()), which loads the entire result set into memory before formatting. This can be a major memory/latency issue for large queries, especially for JSONL/CSV where streaming would be feasible. Consider adding a streaming mode (e.g., using mode="chunk"/"reader" and writing batches incrementally) or at least documenting/guarding against very large result sets.
|
|
||
| payload = _ensure_trailing_nl(_format_table(table, args.output_format)) | ||
| if args.output_file_path: | ||
| with open(args.output_file_path, "w", encoding="utf-8", newline="") as file_handle: | ||
| file_handle.write(payload) | ||
| else: | ||
| stdout.write(payload) |
There was a problem hiding this comment.
New CLI behaviors aren’t fully covered by tests yet: there are no assertions for CSV/pretty formatting, and the --output file-write branch isn’t exercised. Adding tests for these paths would help catch platform/newline differences (CSV) and ensure file output works as intended.
| certs = certs_file.read() | ||
| # Normalize PEM line endings so behavior is stable across platforms. | ||
| return certs.replace(b"\r\n", b"\n").replace(b"\r", b"\n") |
There was a problem hiding this comment.
This change normalizes PEM line endings, but there’s no test ensuring CRLF/CR-only PEM files are normalized to LF. Since tests/test_query.py already asserts certificate loading behavior, it would be good to add a case that writes a cert file with \r\n (and/or \r) and verifies tls_root_certs matches the LF-normalized content.
| with patch("influxdb_client_3.cli.sys.stdout", new=io.StringIO()), patch( | ||
| "influxdb_client_3.cli.sys.stderr", new=io.StringIO() | ||
| ) as stderr: | ||
| rc = main(["query", "SELECT 1"]) |
There was a problem hiding this comment.
test_main_returns_1_when_database_missing calls main() without isolating os.environ. If INFLUXDB3_DATABASE_NAME/INFLUX_DATABASE is set in the test runner environment (e.g., CI), this test will become flaky and may pass when it should fail. Patch os.environ (e.g., @patch.dict('os.environ', {}, clear=True) or at least clear the relevant keys) for the duration of this test to make it deterministic.
| with patch("influxdb_client_3.cli.sys.stdout", new=io.StringIO()), patch( | |
| "influxdb_client_3.cli.sys.stderr", new=io.StringIO() | |
| ) as stderr: | |
| rc = main(["query", "SELECT 1"]) | |
| with patch.dict("os.environ", {}, clear=True): | |
| with patch("influxdb_client_3.cli.sys.stdout", new=io.StringIO()), patch( | |
| "influxdb_client_3.cli.sys.stderr", new=io.StringIO() | |
| ) as stderr: | |
| rc = main(["query", "SELECT 1"]) |
| query_kwargs = {} | ||
| if args.query_timeout is not None: | ||
| query_kwargs["query_timeout"] = args.query_timeout | ||
|
|
There was a problem hiding this comment.
--query-timeout accepts any integer, including negative values, and the CLI forwards it directly into the client options. A negative timeout is invalid and will likely cause confusing downstream errors. Validate args.query_timeout >= 0 and return a clear CLI error when it’s negative.
| def _rows_to_jsonl(rows, fieldnames): | ||
| return "\n".join(json.dumps(row, default=str) for row in rows) | ||
|
|
There was a problem hiding this comment.
For jsonl output with zero rows, _rows_to_jsonl returns an empty string and _ensure_trailing_nl turns that into a single blank line ("\n"). For JSONL, empty results should typically produce no output (or at least avoid emitting a spurious empty record line). Consider special-casing empty rows for JSONL (or adjusting _ensure_trailing_nl to not add a newline for empty payloads).
Proposed Changes
Developers and AI tooling need a simple, scriptable query command for fast feedback during development. Existing community Python CLI seems unmaintained and not aligned with current behavior. So this PR introduces the following:
Checklist